package communication;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;

import Calc.CalcFrame;

class Data implements Serializable {
  private static final long serialVersionUID = 5970177928680172472L;
  public String type;
  public String data;
  
  Data(String type, String data) {
    this.type = type;
    this.data = data;
  }
  
  String getType() {
    return this.type;
  }
  
  String getData() {
    return this.data;
  }
}

public class Communication {
  final String serverIP = "192.168.1.100";
  final int port = 5000;
  private Socket socket = null;
  private ObjectOutputStream output;   
  private ObjectInputStream input;
  private boolean hasInitialedAudio = false;
  private boolean isReceivingAudio = false;
  private boolean hasInitialedCapture = false;
  private boolean isCapturingAudio = false;
  private MyPlayback playback;
  private MyCapture capture;
  
  public void init() {
    try {
      socket = new Socket(InetAddress.getByName(serverIP), port);
      output = new ObjectOutputStream(socket.getOutputStream());
      input = new ObjectInputStream(socket.getInputStream());
      Data init = new Data("init", "");
      sendMsg(init);
    } catch (UnknownHostException e) {
      System.out.println("Unknown Host Address");
      e.printStackTrace();
    } catch (IOException e) {
      System.out.println("IO Exception");
      e.printStackTrace();
    }
  }
  
  public void sendMsg(Data msg) {
    try {
      output.writeObject(msg);
      output.flush();
    } catch (IOException e) {
      System.out.println("Send Message Failed");
      e.printStackTrace();
    }   
  }
  
  public Data receiveMsg() {
    Data data = null;
    try {
      data = (Data)input.readObject();
    } catch (ClassNotFoundException e) {
      System.out.println("Receive: Class Not Found");
      e.printStackTrace();
    } catch (IOException e) {
      System.out.println("Receive: IO Exception");
      e.printStackTrace();
    }
    return data;
  }
  
  public void myPlayBackHandler() {
    if(!hasInitialedAudio) {   
      try {   
        Socket s = new Socket(InetAddress.getByName(serverIP),5000);
        sendMsg(new Data("initialAudio", "")); 
        playback = new MyPlayback(s);   
        playback.start();   
      } catch(Exception e1) {   
        e1.printStackTrace();   
      }   
      hasInitialedAudio = true;   
      isReceivingAudio  = true;      
    } else {   
      if(!isReceivingAudio) {   
        try {   
          playback.notify();   
        } catch(Exception e2) {   
          e2.printStackTrace();   
        }   
        isReceivingAudio = true;   
      } else {                      
        try {   
          playback.wait();   
        } catch(Exception e3) {   
          e3.printStackTrace();   
        }   
        isReceivingAudio = false;   
      }   
    } 
  }
  
  public void myCaptureHandler() {
    if (!hasInitialedCapture) {
      capture = new MyCapture(socket);
      capture.start();
      hasInitialedCapture = true;
      isCapturingAudio = true;
    } else {
      if (!isCapturingAudio) {
        capture.notify();
        isCapturingAudio = true;
      } else {
        try {
          capture.wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        isCapturingAudio = false;
      }
    }
  }
  
  class ReceiveThread extends Thread {
    private CalcFrame client = null;
    private boolean running = true;
  
    ReceiveThread(CalcFrame client) {
      this.client = client;
    }

    public void run() {
      Data data;
      while(running) {
        data = receiveMsg();
        if (data.getType().compareTo("action") == 0) {
          
        } else if (data.getType().compareTo("audioNeed") == 0) {
          
        } else if (data.getType().compareTo("exit") == 0) {
          running = false;
        } else {

        }
      }
    }
  }
}
   
class MyPlayback extends Thread {
  final int bufSize = 16384;    
  private SourceDataLine line;    
  private Socket s;    
 
  MyPlayback(Socket s) {   
    super();   
    this.s=s;    
  }    
 
  public void run() {        
    AudioFormat format = new AudioFormat(8000,16,2,true,true);   
    BufferedInputStream playbackInputStream;    
    try {    
      playbackInputStream=new BufferedInputStream(new AudioInputStream(s.getInputStream(),format,2147483647));
    } catch (IOException ex) {    
      return;    
    }    
    DataLine.Info info = new DataLine.Info(SourceDataLine.class,format);    
    try {    
      line = (SourceDataLine) AudioSystem.getLine(info);    
      line.open(format, bufSize);    
    } catch (LineUnavailableException ex) {    
      return;    
    }    
      
    byte[] data = new byte[1024];
    int numBytesRead = 0;    
    line.start();    
 
    while (this != null) {    
      try {    
        numBytesRead = playbackInputStream.read(data);    
        line.write(data, 0,numBytesRead);    
      } catch (IOException e) {    
        break;    
      }    
    }    
 
    if (this != null) {    
      line.drain();    
    }    
 
    line.stop();    
    line.close();    
    line = null;    
    try {   
      s.close();   
    } catch(Exception e) {   
      e.printStackTrace();   
    }   
  }   
}
  
class MyCapture extends Thread {    
  private TargetDataLine line;    
  private Socket s;    
  private BufferedOutputStream captrueOutputStream;    
   
  MyCapture(Socket s) {   
    super();   
    this.s=s;    
  }    
     
  public void run() {    
    try {    
      captrueOutputStream = new BufferedOutputStream(s.getOutputStream());   
    } catch (IOException ex) {    
      return;    
    }    
   
    System.out.println("s22222");    
    AudioFormat format = new AudioFormat(8000,16,2,true,true);   
    DataLine.Info info = new DataLine.Info(TargetDataLine.class,format);    
   
    try {    
      line = (TargetDataLine) AudioSystem.getLine(info);    
      line.open(format, line.getBufferSize());    
    } catch (Exception ex) {    
      return;    
    }    
   
    byte[] data = new byte[1024];
    int numBytesRead=0;    
    line.start();    
   
    while (this != null) {    
      numBytesRead = line.read(data, 0,1024);
      try {    
        captrueOutputStream.write(data, 0, numBytesRead);
      } catch (Exception ex) {    
        break;    
      }    
    }    
   
    line.stop();    
    line.close();    
    line = null;    
   
    try {    
      captrueOutputStream.flush();    
      captrueOutputStream.close();    
    } catch (IOException ex) {    
      ex.printStackTrace();    
    }    
  }    
}    

